import re
from django.contrib.auth import login, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django.db import DatabaseError
from users.models import User
from libs.captcha.captcha import captcha
from django_redis import get_redis_connection
from home.models import ArticleCategory, Article
import logging
from random import randint
from libs.yuntongxun.sms import CCP
from utils.response_code import RETCODE

logger=logging.getLogger('django')
# Create your views here.


class ImageCodeView(View):

    def get(self, request):

        # 获取前端传递过来的参数
        uuid = request.GET.get('uuid')

        # 判断参数是否为None
        if uuid is None:
            return HttpResponseBadRequest('请求参数错误')

        # 获取验证码内容和验证码图片的二进制数据
        text, image = captcha.generate_captcha()

        # 将图片验证内容保存到redis中, 并设置过期时间
        redis_conn = get_redis_connection('default')
        redis_conn.setex('img:%s' % uuid, 300, text)

        # 返回响应, 将生成的图片以content_type为image/jpeg的形式返回给请求
        return HttpResponse(image, content_type='image/jpeg')


class SmsCodeView(View):

    def get(self,request):
        # 接收参数
        image_code_client = request.GET.get('image_code')
        uuid = request.GET.get('uuid')
        mobile=request.GET.get('mobile')

        # 校验参数
        if not all([image_code_client, uuid,mobile]):
            return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})

        # 创建连接到redis的对象
        redis_conn = get_redis_connection('default')
        # 提取图形验证码
        image_code_server = redis_conn.get('img:%s' % uuid)
        if image_code_server is None:
            # 图形验证码过期或者不存在
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
        # 删除图形验证码，避免恶意测试图形验证码
        try:
            redis_conn.delete('img:%s' % uuid)
        except Exception as e:
            logger.error(e)
        # 对比图形验证码
        image_code_server = image_code_server.decode()  # bytes转字符串
        if image_code_client.lower() != image_code_server.lower():  # 转小写后比较
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})

        # 生成短信验证码：生成6位数验证码
        sms_code = '%06d' % randint(0, 999999)
        #将验证码输出在控制台，以方便调试
        logger.info(sms_code)
        # 保存短信验证码到redis中，并设置有效期
        redis_conn.setex('sms:%s' % mobile, 300, sms_code)
        # 发送短信验证码
        CCP().send_template_sms(mobile, [sms_code, 5],1)

        # 响应结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})


class RegisterView(View):

    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')

        # 校验参数
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')

        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return HttpResponseBadRequest('请输入8-20位的密码')

        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        # 保存数据
        try:
            user = User.objects.create_user(
                username=mobile,
                mobile=mobile,
                password=password
            )
        except DatabaseError:
            return HttpResponseBadRequest('注册失败')

        # 返回响应
        login(request, user)

        return redirect(reverse('home:home'))


class LoginView(View):

    def get(self, request):

        return render(request, 'login.html')

    def post(self, request):

        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        remember = request.POST.get('remember')

        # 验证参数
        if not all([mobile, password]):
            return HttpResponseBadRequest('缺少必传参数')

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号')

        if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
            return HttpResponseBadRequest('密码最少8位，最长20位')

        # 验证登录
        from django.contrib.auth import authenticate
        # 登录验证字段默认的是 username 和 password
        # 当前的需求是验证 mobile 和 password
        user = authenticate(mobile=mobile, password=password)
        if user is None:
            return HttpResponseBadRequest('用户名或密码不正确')

        # 状态保持
        login(request, user)

        # 响应登录结果
        # 根据登录的next参数设置登录跳转路由
        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('home:home'))

        # 设置状态保持的周期
        if remember != 'on':
            # 没有记住用户: 浏览器会话结束就过期
            request.session.set_expiry(0)
            # 设置cookie
            response.set_cookie('is_login', True)
            response.set_cookie('username', user.username, max_age=30*24*3600)

        else:
            # 记住用户: None表示两周后过期
            request.session.set_expiry(None)
            # 设置cookie
            response.set_cookie('is_login', True, max_age=14*24*3600)
            response.set_cookie('username', user.username, max_age=30*24*3600)

        # 返回响应
        return response


class LogoutView(View):
    """
        由于首页中登录状态是从cookie中读取的。
        所以退出登录时，需要将cookie中登录状态清除
    """

    def get(self, request):
        # 清理session
        logout(request)
        # 退出登录, 重定向到登录页
        response = redirect(reverse('home:home'))
        # 退出登录时清除cookie中的登录状态
        response.delete_cookie('is_login')
        response.delete_cookie('username')

        return response


class UserCenterView(LoginRequiredMixin, View):

    def get(self, request):
        # 获取用户信息
        user = request.user

        # 组织模板渲染数据
        context = {
            'username': user.username,
            'mobile': user.mobile,
            'avatar': user.avatar.url if user.avatar else None,
            'user_desc': user.user_desc
        }

        return render(request, 'center.html', context=context)

    def post(self, request):
        # 接受数据
        user = request.user
        avatar = request.FILES.get('avatar')
        username = request.POST.get('username', user.username)
        user_desc = request.POST.get('desc', user.user_desc)

        # 修改数据库数据
        try:
            user.username = username
            user.user_desc = user_desc
            if avatar:
                user.avatar = avatar
            user.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('更新失败, 请稍后再试')

        # 返回响应, 刷新页面
        response = redirect(reverse('users:center'))

        # 更新cookie信息
        response.set_cookie('username', user.username, max_age=30*24*3600)

        return response


class WriteBlogView(LoginRequiredMixin, View):

    def get(self, request):
        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        context = {
            'categories': categories
        }

        return render(request, 'write_blog.html', context=context)

    def post(self, request):

        # 接收参数
        user = request.user
        title = request.POST.get('title')
        avatar = request.FILES.get('avatar')
        category_id = request.POST.get('category')
        tags = request.POST.get('tags')
        sumary = request.POST.get('sumary')
        content = request.POST.get('content')

        # 验证数据
        if not all([title, avatar, category_id, tags, sumary, content]):
            return HttpResponseBadRequest('参数不全')

        # 判断文章分类id数据是否正确
        try:
            article_category = ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有此分类信息')

        # 保存到数据库
        try:
            article = Article.objects.create(
                author=user,
                avatar=avatar,
                category=article_category,
                tags=tags,
                title=title,
                sumary=sumary,
                content=content
            )
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('发布失败, 请稍后重试')

        # 返回响应, 跳转到文章详情页面
        # 暂时先跳转到首页
        return redirect(reverse('home:home'))


class ForgetPasswordView(View):

    def get(self, request):

        return render(request, 'forget_password.html')

    def post(self, request):
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')

        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')

        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('请输入8-20位的密码')

        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        # 根据手机号查询数据
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 如果该手机号不存在，则注册个新用户
            try:
                User.objects.create_user(username=mobile, mobile=mobile, password=password)
            except Exception:
                return HttpResponseBadRequest('修改失败，请稍后再试')
        else:
            # 修改用户密码
            user.set_password(password)
            user.save()

        # 跳转到登录页面
        response = redirect(reverse('users:login'))

        return response